﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Newtonsoft.Json.Linq;
using OpenPasteSpider.projectmodel;
using Volo.Abp.DependencyInjection;
using Volo.Abp.ObjectMapping;
using Z.EntityFramework.Plus;

namespace OpenPasteSpider
{
    /// <summary>
    /// 综合服务
    /// </summary>
    public class ActionHelper : ISingletonDependency, IDisposable
    {

        /// <summary>
        /// 
        /// </summary>
        private readonly ILogger<ActionHelper> _logger;
        /// <summary>
        /// 
        /// </summary>
        private readonly LinkHelper _linkHelper;
        /// <summary>
        /// 
        /// </summary>
        private readonly IObjectMapper _objectMapper;
        /// <summary>
        /// 
        /// </summary>
        private readonly SpiderConfig _config;
        /// <summary>
        /// 
        /// </summary>
        private readonly ChannelHelper _channelHelper;
        /// <summary>
        /// 
        /// </summary>
        private readonly PublicModelHelper _modelHelper;

        /// <summary>
        /// 
        /// </summary>
        private readonly IHttpClientFactory _httpClientFactory;


        /// <summary>
        /// 
        /// </summary>
        /// <param name="logger"></param>
        /// <param name="config"></param>
        /// <param name="linkHelper"></param>
        /// <param name="httpClientFactory"></param>
        /// <param name="objectMapper"></param>
        /// <param name="modelHelper"></param>
        /// <param name="channelHelper"></param>
        public ActionHelper(
            //IServiceProvider serviceProvider,
            ILogger<ActionHelper> logger,
            IOptions<SpiderConfig> config,
            LinkHelper linkHelper,
            IHttpClientFactory httpClientFactory,
            IObjectMapper objectMapper,
            PublicModelHelper modelHelper,
            ChannelHelper channelHelper)
        {
            //_serviceProvider = serviceProvider;
            _logger = logger;
            _linkHelper = linkHelper;
            //_cache = cache;
            _objectMapper = objectMapper;
            _modelHelper = modelHelper;
            _channelHelper = channelHelper;
            _config = config.Value;
            _httpClientFactory = httpClientFactory;
        }

        public LinkHelper LinkHelper { get { return _linkHelper; } }




        /// <summary>
        /// 构建镜像并升级
        /// </summary>
        /// <param name="_dbContext"></param>
        /// <param name="serviceid"></param>
        /// <param name="storeid"></param>
        /// <param name="modelid">升级某一个环境</param>
        /// <param name="autoupdate"></param>
        /// <param name="_models">多个环境用逗号隔开</param>
        /// <param name="_clonecommandstr">克隆代码的前置命令，在服务的前置之前</param>
        /// <returns></returns>
        /// <exception cref="PasteException"></exception>
        public async Task<int> ImageBuild(IOpenPasteSpiderDbContext _dbContext, int serviceid, int storeid = 0, int modelid = 0, bool autoupdate = false, string _models = "", string _clonecommandstr = "")
        {

            if (serviceid == 0)
            {
                throw new PasteException("ServiceId 和 StoreId 不能为空，表示需要在哪个私有仓库构建哪个服务的镜像");
            }
            if (autoupdate && modelid == 0 && String.IsNullOrEmpty(_models))
            {
                throw new PasteException("构建后自动升级需要指定升级服务的哪个环境");
            }
            //using var scope = _serviceProvider.CreateScope();
            //using var _dbContext = scope.ServiceProvider.GetRequiredService<IOpenPasteSpiderDbContext>();
            _logger.LogWarning($"build image serviceid is {serviceid}: StoreId is:{storeid}");
            var service = await _modelHelper.ItemServiceInfo(serviceid, _dbContext);
            if (service == null || service == default)
            {
                throw new PasteException($"没有找到这个服务,ID:{serviceid}，无法继续执行后续操作！");
            }
            if (service.FileModel == 0 || service.FileModel == 3)
            {
                throw new PasteException($"服务ID:{serviceid}，文件类型为静态或者镜像模式，不需要构建镜像，无法继续执行后续操作！");
            }
            //读取这个仓储挂载的服务器
            //StoreInfoDto _setting = await _modelHelper.ItemStoreInfo(storeid, _dbContext);
            LinuxInfoDto linux = await _modelHelper.ItemLinuxInfo(1, _dbContext);
            //BindServiceImage bindimageservice = null;
            var buildversion = service.Version < 1000 ? 1000 : service.Version + 1;
            service.Version = buildversion;
            var project = service.Project;
            service.ImagePrefix = $"{project.Code}_{service.Code}";
            //var linux = _dbContext.LinuxInfo.Where(x => x.IsRegistryLinux).FirstOrDefault();
            if (linux == null || linux == default) { throw new PasteException($"没有配置构建服务器，无法继续构建镜像，请确认配置!"); }
            var one = new BindServiceImage()
            {
                BuildState = 0,
                CreateDate = DateTime.Now,
                ImageDigest = "",
                IsEnable = false,
                ServiceId = serviceid,
                Version = buildversion,
                StoreId = storeid
            };
            _dbContext.Add(one);
            var plan = new PlanInfo
            {
                AppId = 0,
                CreateDate = DateTime.Now,
                Durtion = 0,
                ModelId = modelid,
                OrderModel = OrderModel.buildimage,
                ServiceId = serviceid,
                StoreId = 0,
                Version = buildversion,
                LinuxId = linux.Id,
                ProjectId = project.Id
            };
            _dbContext.Add(plan);
            await _dbContext.SaveChangesAsync();
            var _planid = plan.Id;// Guid.NewGuid().ToString();
            var _zindex = 0;
            ////前置命令  build tag push tag s
            //处理头部命令
            var comandbefore = new List<PlanItem>();
            if (!String.IsNullOrEmpty(_clonecommandstr))
            {
                var befors = _clonecommandstr.SplitByLine();
                for (var j = 0; j < befors.Count; j++)
                {
                    if (!String.IsNullOrEmpty(befors[j]))
                    {
                        if (!befors[j].StartsWith("#"))
                        {
                            comandbefore.Add(
                                _buildPlanItemModel(
                                    _action: ActionState.custommodel,
                                    _planid: _planid,
                                    _command: _modelHelper.BuildCommandString(
                                        befors[j],
                                        _linux: linux,
                                        _service: service
                                        ),
                                    _zindex: _zindex++
                                    )
                                );
                        }
                    }
                }
            }

            //整理，写入前置命令 服务的前置命令
            if (!String.IsNullOrEmpty(service.BeforeCommands))
            {
                var befors = service.BeforeCommands.SplitByLine();
                for (var j = 0; j < befors.Count; j++)
                {
                    if (!String.IsNullOrEmpty(befors[j]))
                    {
                        comandbefore.Add(
                            _buildPlanItemModel(
                                _action: ActionState.custommodel,
                                _planid: _planid,
                                _command: _modelHelper.BuildCommandString(
                                    befors[j],
                                    _linux: linux,
                                    _service: service
                                    ),
                                _zindex: _zindex++
                                )
                            );
                    }
                }
            }
            //构建新的镜像版本
            comandbefore.Add(_buildPlanItemModel(_action: ActionState.dockerbuild, _planid: _planid, _command: $"docker build -t {service.ImagePrefix}:{buildversion} {linux.WorkDir}publish/{project.Code}/{service.Code}", _zindex: _zindex++));
            //判断镜像是否存在 host:5000/xxxx:version
            comandbefore.Add(_buildPlanItemModel(_action: ActionState.imageexist, _planid: _planid, _command: $"{service.ImagePrefix}:{buildversion}",_target:$"{buildversion}", _zindex: _zindex++));

            //删除本地的
            if (buildversion > 1000)
            {
                var delversion = buildversion - service.SaveNumber;
                if (delversion >= 1000)
                {
                    if (linux != null && linux != default && linux.Tool == "podman")
                    {
                        comandbefore.Add(_buildPlanItemModel(_action: ActionState.dockerrmi, _planid: _planid, _command: $"docker rmi localhost/{service.ImagePrefix}:{(delversion)}", _zindex: _zindex++, _strict: false, _target: $"{service.ImagePrefix}:{(delversion)}"));
                    }
                    else
                    {
                        comandbefore.Add(_buildPlanItemModel(_action: ActionState.dockerrmi, _planid: _planid, _command: $"docker rmi {service.ImagePrefix}:{(delversion)}", _zindex: _zindex++, _strict: false, _target: $"{service.ImagePrefix}:{(delversion)}"));
                    }
                }
            }

            //添加后置命令
            if (!String.IsNullOrEmpty(service.AfterCommands))
            {
                var befors = service.AfterCommands.SplitToLines();
                for (var j = 0; j < befors.Length; j++)
                {
                    comandbefore.Add(_buildPlanItemModel(_action: ActionState.custom, _planid: _planid, _command: _modelHelper.BuildCommandString(befors[j], _linux: linux, _service: service), _zindex: _zindex++));
                }
            }

            //通知构建结果
            comandbefore.Add(_buildPlanItemModel(_action: ActionState.buildnotify, _planid: _planid, _command: $"【构建通知】构建镜{service.ImagePrefix}:{buildversion}像成功.{(autoupdate ? "正在准备自动升级版本... .. ." : "没有配置自动升级版本，请手动点击!")}", _zindex: _zindex++));

            //是否直接自动升级
            if (autoupdate)
            {
                //单个环境要升级
                if (modelid != 0)
                {
                    comandbefore.Add(_buildPlanItemModel(_action: ActionState.buildupdate, _planid: _planid, _command: $"{buildversion}", _zindex: _zindex++));
                }
                //多个环境要升级
                if (!String.IsNullOrEmpty(_models))
                {
                    comandbefore.Add(_buildPlanItemModel(_action: ActionState.buildupdates, _planid: _planid, _command: $"{_models}", _zindex: _zindex++));
                }
            }
            _dbContext.AddRange(comandbefore);
            await _dbContext.SaveChangesAsync();
            _channelHelper.WritePlanCommand(plan.Id, plan.LinuxId);
            await _dbContext.ServiceInfo.Where(x => x.Id == serviceid).UpdateAsync(x => new ServiceInfo() { Version = buildversion });
            await _modelHelper.ItemServiceClean(serviceid);
            _modelHelper.PlanRunTag(plan);
            return 1;
        }

        /// <summary>
        /// 升级版本
        /// </summary>
        /// <param name="_dbContext"></param>
        /// <param name="modelid">哪一个模块</param>
        /// <param name="version">非0表示指定版本</param>
        /// <param name="num">大于0表示灰度发布</param>
        /// <returns></returns>
        public async Task<int> ModelUpdate(IOpenPasteSpiderDbContext _dbContext, int modelid, int version = 0, int num = 0)
        {

            var _serviceid = 0;

            ServiceInfoDto service = null;
            var model = await _dbContext.ModelInfo.Where(x => x.Id == modelid).Include(x => x.Service).Include(x => x.Service.Project).AsNoTracking().FirstOrDefaultAsync();

            if (model == null || model == default)
            {
                throw new PasteException("没有找到这个服务的这个环境配置信息，请确认！");
            }

            if (model.Service != null)
            {
                _serviceid = model.Service.Id;
                service = _objectMapper.Map<ServiceInfo, ServiceInfoDto>(model.Service);
            }
            else
            {
                throw new PasteException($"没有为这个环境ID:{modelid}找到有效的服务信息，请重试！");
            }

            //没有指定版本，则从最新版本中拿
            if (version == 0 && service.FileModel != 3)
            {
                //读取可用的最新版本
                var imageservice = _dbContext.BindServiceImage.Where(x => x.ServiceId == _serviceid).OrderByDescending(x => x.Version).AsNoTracking().FirstOrDefault();
                if (imageservice != null && imageservice != default)
                {
                    version = imageservice.Version;
                }
            }

            if (service.FileModel == 3)
            {
                if (String.IsNullOrEmpty(service.DirectImage))
                {
                    throw new PasteException($"服务ID:{service.Id}，设定的文件模式为镜像模式，但是镜像名称为空，无法继续执行接下来的操作！！！");
                }
            }

            //分别处理服务器内 按照设置 拆分成N份

            var querylinux = _dbContext.BindModelLinux.Where(x => x.ModelId == modelid && x.IsEnable).Select(x => x.LinuxId);

            var linuxs = await _dbContext.LinuxInfo.Where(x => querylinux.Contains(x.Id)).ToListAsync();

            if (linuxs != null && linuxs.Count > 0)
            {
                if (service.FileModel != 3)
                {
                    var oldimageinfo = _dbContext.BindServiceImage.Where(x => x.Version == version && x.ServiceId == _serviceid).FirstOrDefault();
                    if (oldimageinfo == null || oldimageinfo == default)
                    {
                        throw new PasteException("当前指定版本的镜像不存在，无法继续执行升级的任务！");
                    }
                }

                foreach (var linux in linuxs)
                {
                    var zindex = 0;
                    var comands = new List<PlanItem>();
                    var plan = new PlanInfo
                    {
                        AppId = 0,
                        CreateDate = DateTime.Now,
                        Durtion = 0,
                        ModelId = modelid,
                        OrderModel = OrderModel.update,
                        ServiceId = service.Id,
                        StoreId = 0,
                        Version = version,
                        LinuxId = linux.Id,
                        ProjectId = service.Project.Id
                    };
                    _dbContext.Add(plan);
                    await _dbContext.SaveChangesAsync();
                    var planid = plan.Id;
                    //如果现在没有在运行的，则至少拿一个出来运行... .. .
                    var apps = await _dbContext.AppInfo.Where(x => (x.StateCode == RunState.running || x.StateCode == RunState.prestop) && x.ModelId == modelid && x.LinuxId == linux.Id).AsNoTracking().ToListAsync();
                    //处理头部命令
                    var comandbefore = new List<PlanItem>();
                    var newlist = new List<PlanItem>();
                    if (apps != null && apps.Count > 0)
                    {
                        #region 升级现有运行的docker服务

                        var commanditem = new List<PlanItem>();

                        foreach (var one in apps)
                        {
                            commanditem.Add(_buildPlanItemModel(_action: ActionState.sqlstop, _planid: planid, _zindex: zindex++, _appid: one.Id));
                            //等待nginx的缓冲完成
                            commanditem.Add(_buildPlanItemModel(_action: ActionState.waitdely, _planid: planid, _zindex: zindex++));
                            //stop
                            commanditem.Add(_buildPlanItemModel(_action: ActionState.dockerstop, _planid: planid, _command: $"docker stop {one.AppID}", _target: $"{one.AppID}", _zindex: zindex++, _strict: false));
                            //rm
                            commanditem.Add(_buildPlanItemModel(_action: ActionState.dockerrm, _planid: planid, _command: $"docker rm {one.AppID}", _target: $"{one.AppID}", _zindex: zindex++, _strict: false));
                            //run
                            commanditem.Add(_buildPlanItemModel(_action: ActionState.dockerrun, _planid: planid, _zindex: zindex++, _appid: one.Id));
                            //... .. .等待启动
                            commanditem.Add(_buildPlanItemModel(_action: ActionState.dockerwaitrun, _planid: planid, _zindex: zindex++, _appid: one.Id));
                            //改数据库状态
                            commanditem.Add(_buildPlanItemModel(_action: ActionState.sqlrun, _planid: planid, _command: $"{version}", _zindex: zindex++, _appid: one.Id));
                        }
                        //总共分几次升级 最少也分三次，每次至少一个，不足的则跳过
                        var tosplitnum = service.UpdateSplits;
                        if (tosplitnum < 3)
                        {
                            tosplitnum = 3;
                        }
                        //每份大小
                        var persplitsize = apps.Count / tosplitnum;//每一次的大小
                        if (persplitsize < 1) { persplitsize = 1; }
                        for (var t = 0; t < tosplitnum; t++)
                        {
                            var indexstart = t * persplitsize;
                            var size = apps.Count - indexstart;
                            if (t != tosplitnum - 1) { size = persplitsize; }
                            if (size > 0 && indexstart < apps.Count)
                            {
                                newlist.AddRange(commanditem.Where(x => x.ActionType == ActionState.sqlstop).OrderBy(x => x.RunIndex).ToList().GetRange(indexstart, size));
                                newlist.Add(_buildPlanItemModel(_action: ActionState.waitdely, _planid: planid, _zindex: zindex++));
                                newlist.AddRange(commanditem.Where(x => x.ActionType == ActionState.dockerstop).OrderBy(x => x.RunIndex).ToList().GetRange(indexstart, size));
                                newlist.AddRange(commanditem.Where(x => x.ActionType == ActionState.dockerrm).OrderBy(x => x.RunIndex).ToList().GetRange(indexstart, size));
                                newlist.AddRange(commanditem.Where(x => x.ActionType == ActionState.dockerrun).OrderBy(x => x.RunIndex).ToList().GetRange(indexstart, size));
                                newlist.AddRange(commanditem.Where(x => x.ActionType == ActionState.dockerwaitrun).OrderBy(x => x.RunIndex).ToList().GetRange(indexstart, size));
                                newlist.AddRange(commanditem.Where(x => x.ActionType == ActionState.sqlrun).OrderBy(x => x.RunIndex).ToList().GetRange(indexstart, size));
                            }
                        }
                        #endregion
                    }
                    else
                    {
                        comands.Clear();
                    }
                    //处理尾巴部分
                    var commandafter = new List<PlanItem>();
                    //按照顺序写入到统一的队列中
                    comands.AddRange(comandbefore);
                    comands.AddRange(newlist);
                    if (commandafter != null && commandafter.Count > 0)
                    {
                        comands.AddRange(commandafter);
                    }
                    //重置他们的排序问题
                    zindex = 0;
                    foreach (var item in comands)
                    {
                        item.RunIndex = zindex++;
                    }
                    service.Version = version;
                    if (comands.Count > 0)
                    {
                        await _dbContext.AddRangeAsync(comands.ToArray());
                        await _dbContext.SaveChangesAsync();
                        //ChannelData.WriteOrder(_planid, linux.GroupName);
                        _channelHelper.WritePlanCommand(plan.Id, plan.LinuxId);
                        _modelHelper.PlanRunTag(plan);
                    }
                }
            }
            else
            {
                throw new PasteException("当前环境没有绑定服务器，请先绑定后在升级！");
            }
            return 1;
        }

        /// <summary>
        /// 重启一个服务
        /// </summary>
        /// <param name="_dbContext"></param>
        /// <param name="appid"></param>
        /// <param name="appidcode">container.ID</param>
        /// <returns></returns>
        public async Task<int> ContainerRestart(IOpenPasteSpiderDbContext _dbContext, int appid, string appidcode = "")
        {
            var app = _dbContext.AppInfo.WhereIf(appid != 0, x => x.Id == appid).WhereIf(!string.IsNullOrEmpty(appidcode), x => x.AppID == appidcode).AsNoTracking().FirstOrDefault();
            if (app != null)
            {
                var plan = new PlanInfo
                {
                    CreateDate = DateTime.Now,
                    Durtion = 0,
                    ModelId = app.ModelId,
                    OrderModel = OrderModel.restart,
                    ServiceId = 0,
                    AppId = app.Id,
                    StoreId = 0,
                    LinuxId = app.LinuxId,
                    Version = 0,
                    ProjectId = app.ProjectId
                };
                _dbContext.Add(plan);
                await _dbContext.SaveChangesAsync();
                var _planid = plan.Id;
                var zindex = 0;
                var commands = new List<PlanItem>();
                commands.Add(_buildPlanItemModel(_action: ActionState.sqlstop, _planid: _planid, _zindex: zindex++, _appid: app.Id));
                commands.Add(_buildPlanItemModel(_action: ActionState.waitdely, _planid: _planid, _zindex: zindex++));
                commands.Add(_buildPlanItemModel(_action: ActionState.dockerrestart, _planid: _planid, _command: $"docker restart {app.AppID}", _target: $"{app.AppID}", _zindex: zindex++));
                commands.Add(_buildPlanItemModel(_action: ActionState.dockerwaitrun, _planid: _planid, _command: $"{app.AppID}", _zindex: zindex++, _appid: app.Id));
                commands.Add(_buildPlanItemModel(_action: ActionState.sqlrun, _planid: _planid, _command: $"{app.Version}", _zindex: zindex++, _appid: app.Id));
                _dbContext.AddRange(commands);
                await _dbContext.SaveChangesAsync();
                _channelHelper.WritePlanCommand(plan.Id, plan.LinuxId);
                _modelHelper.PlanRunTag(plan);
            }
            else
            {
                _logger.LogWarning($"没有找到docker为{appid},无法进行添加重启的任务.");
            }
            return 1;
        }


        /// <summary>
        /// 为服务添加container
        /// </summary>
        /// <param name="_dbContext"></param>
        /// <param name="serviceid"></param>
        /// <param name="modelcode"></param>
        /// <param name="num"></param>
        /// <param name="linuxid"></param>
        /// <returns></returns>
        public async Task<int> AddContainer(IOpenPasteSpiderDbContext _dbContext, int serviceid, string modelcode = "default", int num = 1, int linuxid = 0)
        {
            var modelid = _dbContext.ModelInfo.Where(x => x.Service.Id == serviceid && x.Code == modelcode).Select(x => x.Id).FirstOrDefault();
            if (modelid != 0)
            {
                await AddRunAppAsync(_dbContext, modelid, num, linuxid);
            }
            else
            {
                throw new PasteException("当前服务不存在此环境信息，请重试！");
            }
            return 1;
        }

        /// <summary>
        /// 对现有的服务进行扩容
        /// </summary>
        /// <param name="_dbContext"></param>
        /// <param name="modelid"></param>
        /// <param name="num"></param>
        /// <param name="linuxid"></param>
        /// <returns></returns>
        public async Task<int> AddRunAppAsync(IOpenPasteSpiderDbContext _dbContext, int modelid, int num, int linuxid = 0)
        {
            var _finalynum = 0;
            var _serviceid = 0;
            var model = await _dbContext.ModelInfo.Where(x => x.Id == modelid).Include(x => x.Service).Include(x => x.Service.Project).FirstOrDefaultAsync();
            var service = model.Service;
            var projectid = service.Project.Id;
            _serviceid = service.Id;
            //先要判断镜像是否存在。。。 。。 。
            BindServiceImage _imagedto = null;
            if (service.FileModel != 3)
            {
                _imagedto = _dbContext.BindServiceImage.Where(x => x.ServiceId == _serviceid && x.IsEnable).OrderByDescending(x => x.Version).AsNoTracking().FirstOrDefault();
                if (_imagedto == null || _imagedto == default)
                {
                    throw new PasteException($"对服务 serviceid:{_serviceid}进行的扩容操作失败，没有这个服务的对应的镜像，无法继续操作！");
                }
            }
            var bindservicelinux = _dbContext.BindModelLinux.Where(x => x.ModelId == modelid && x.IsEnable).WhereIf(linuxid > 0, x => x.LinuxId == linuxid).AsNoTracking().ToList();
            if (bindservicelinux != null && bindservicelinux != default)
            {

                var splitrate = new int[bindservicelinux.Count];
                for (var k = 0; k < bindservicelinux.Count; k++)
                {
                    splitrate[k] = bindservicelinux[k].Weight;
                }

                var newnums = _splitnumbertoarray(num, splitrate);

                for (var k = 0; k < newnums.Length; k++)
                {
                    var item = bindservicelinux[k];
                    var neednum = newnums[k];//需要启动的数量
                    var commands = new List<PlanItem>();
                    var zindex = 0;
                    var plan = new PlanInfo
                    {
                        CreateDate = DateTime.Now,
                        Durtion = 0,
                        ModelId = modelid,
                        OrderModel = num > 0 ? OrderModel.enlarge : OrderModel.reduce,
                        ServiceId = _serviceid,
                        AppId = 0,
                        StoreId = _imagedto != null ? _imagedto.StoreId : 0,
                        LinuxId = item.LinuxId,
                        Version = _imagedto != null ? _imagedto.Version : 0,
                        ProjectId = projectid
                    };
                    _dbContext.Add(plan);
                    await _dbContext.SaveChangesAsync();
                    var planid = plan.Id;
                    //这台服务器剩余可以运行的数量
                    var linuxservicerunlessnum = item.LimitMaxNum;

                    commands.Add(_buildPlanItemModel(_action: ActionState.linuxcacheclean, _planid: plan.Id, _command: $"echo 1 > /proc/sys/vm/drop_caches", _zindex: zindex++));

                    //兼容剩余资源是否满足
                    commands.Add(_buildPlanItemModel(_action: ActionState.checklinuxfree, _planid: planid, _command: $"", _zindex: zindex++));
                    //还剩需要启动的
                    if (neednum > 0)
                    {
                        if (linuxservicerunlessnum > 0)
                        {
                            //获取可以运行的数量，可以运行的数量不会大于允许运行的数量
                            neednum = neednum > linuxservicerunlessnum ? linuxservicerunlessnum : neednum;
                            for (var i = 0; i < neednum; i++)
                            {
                                _finalynum++;
                                //新建一个端口
                                var app = new AppInfo
                                {
                                    Address = "",
                                    LinuxId = plan.LinuxId,
                                    Version = service.Version,
                                    ModelId = modelid,
                                    ProjectId = projectid,
                                    ServiceId = service.Id,
                                    StateCode = RunState.waitrun,
                                    Name = ""
                                };
                                await _dbContext.AddAsync(app);
                                await _dbContext.SaveChangesAsync();
                                if (app != null && app != default && app.Id > 0)
                                {
                                    commands.Add(_buildPlanItemModel(_action: ActionState.dockerrun, _planid: planid, _zindex: zindex++, _appid: app.Id));
                                    //... .. .等待启动
                                    commands.Add(_buildPlanItemModel(_action: ActionState.dockerwaitrun, _planid: planid, _zindex: zindex++, _appid: app.Id));
                                    //改数据库状态
                                    commands.Add(_buildPlanItemModel(_action: ActionState.sqlrun, _planid: planid, _zindex: zindex++, _appid: app.Id));
                                }
                                else
                                {
                                    _logger.LogError("line.1676.新增的app写入失败，造成扩容失败！ the new apppoint add failed!");
                                }
                            }
                        }
                        else
                        {
                            _logger.LogWarning($"系统需要扩容{neednum}，但是当前服务器剩余容量{linuxservicerunlessnum}为不足，扩容未必按照数据进行!");
                        }
                    }
                    //等于3一般是报废的命令，注意看头和尾巴 就3个了
                    if (commands.Count > 3)
                    {
                        await _dbContext.AddRangeAsync(commands.ToArray());
                        await _dbContext.SaveChangesAsync();
                        _channelHelper.WritePlanCommand(plan.Id, plan.LinuxId);
                        _modelHelper.PlanRunTag(plan);
                    }
                }
            }
            else
            {
                new PasteException("请先为环境绑定需要部署的服务器", CodeLevel.ShowInfo);
            }
            //循环执行扩容操作
            return _finalynum;
        }

        /// <summary>
        /// 获取可用的端口
        /// </summary>
        /// <param name="openPorts">开放的端口区间</param>
        /// <param name="num">需要获取的个数</param>
        /// <param name="usedports">已经被占用的</param>
        public int[] CountCanUsePort(string openPorts, int num, int[] usedports)
        {
            var _hitports = new List<int>();
            foreach (var item in openPorts.Split(","))
            {
                if (item.Contains("-"))
                {
                    int.TryParse(item.Split("-")[0], out var stark);
                    int.TryParse(item.Split("-")[1], out var endk);
                    if (endk > 0 && endk > stark && stark > 0)
                    {
                        for (var k = stark; k <= endk; k++)
                        {
                            if (!usedports.Contains(k))
                            {
                                _hitports.Add(k);
                                if (_hitports.Count == num)
                                {
                                    break;
                                }
                            }
                        }
                    }
                }
                else
                {
                    if (int.TryParse(item, out var port))
                    {
                        if (!usedports.Contains(port))
                        {
                            _hitports.Add(port);
                            if (_hitports.Count == num)
                            {
                                break;
                            }
                        }
                    }
                }
            }
            return _hitports.ToArray();
        }

        /// <summary>
        /// 清理服务器缓存 echo 3 > /proc/sys/vm/drop_caches
        /// </summary>
        /// <param name="linuxid"></param>
        /// <returns></returns>
        public async Task<(bool run, string message)> LinuxCleanCache(int linuxid)
        {

            var strbody = await RunCommandAsync($"echo 3 > /proc/sys/vm/drop_caches", linuxid);

            if (strbody.Item1.Length > 0)
            {
                return (true, strbody.Item1);
            }
            return (false, strbody.Item1);
        }

        /// <summary>
        /// 减配
        /// </summary>
        /// <param name="_dbContext"></param>
        /// <param name="modelid">环境ID</param>
        /// <param name="num">减配数量大于0</param>
        /// <param name="linuxid">非0只减配这个服务器的</param>
        /// <returns></returns>
        public async Task<int> RemoveRunApp(IOpenPasteSpiderDbContext _dbContext, int modelid, int num, int linuxid = 0)
        {
            var _finalynum = num;
            if (num <= 0)
            {
                return 1;
            }
            //_logger.LogInformation($"接收到对服务环境{modelid}的减配命令，减配目标{num}");
            var modeldto = await _modelHelper.ItemModelInfoAsync(modelid, _dbContext);
            var projectid = modeldto.ProjectId;

            var binds = await (from bind in _dbContext.BindModelLinux.Where(x => x.ModelId == modelid)
                               join b in _dbContext.ModelInfo.Where(x => 1 == 1) on bind.ModelId equals b.Id into abc
                               from model in abc.DefaultIfEmpty()
                               select new
                               {
                                   limitminnum = bind.LimitMinNum,
                                   limitmaxnum = bind.LimitMaxNum,
                                   linuxid = bind.LinuxId
                               }).ToListAsync();

            if (binds != null && binds.Count > 0)
            {
                //最小需要保留的总数
                var lessrun = binds.Sum(x => x.limitminnum);

                var nowruns = _dbContext.AppInfo.Where(x => x.ModelId == modelid && x.StateCode == RunState.running).AsNoTracking().ToList();

                //当前运行的总数
                var nowruntotal = nowruns.Count();

                //计算出要减少的数量，或者说满足目标的最接近数量
                var canminusnum = num > nowruntotal - lessrun ? nowruntotal - lessrun : num;
                _logger.LogInformation($"对服务环境{modelid}计算出预减配的数量为：{canminusnum}");
                var datainfo = new List<MinusInfoModel>();
                if (linuxid == 0)
                {
                    foreach (var item in binds)
                    {

                        var one = new MinusInfoModel();
                        one.totalrunnum = nowruns.Count(x => x.LinuxId == item.linuxid);
                        one.linuxid = item.linuxid;
                        one.totalkeepnum = item.limitminnum;
                        one.canminusnum = one.totalrunnum - one.totalkeepnum;
                        one.projectid = projectid;
                        if (one.canminusnum < 0) { one.canminusnum = 0; }
                        datainfo.Add(one);
                    }
                }
                else
                {
                    var item = binds.Where(x => x.linuxid == linuxid).FirstOrDefault();
                    if (item == null || item == default)
                    {
                        throw new PasteException("当前指定服务器没有运行的容器，无法进行减配操作");
                    }
                    var one = new MinusInfoModel();
                    one.totalrunnum = nowruns.Count(x => x.LinuxId == item.linuxid);
                    one.linuxid = item.linuxid;
                    one.totalkeepnum = item.limitminnum;
                    one.canminusnum = one.totalrunnum - one.totalkeepnum;
                    one.projectid = projectid;
                    if (one.canminusnum < 0) { one.canminusnum = 0; }
                    datainfo.Add(one);
                }


                var boolcancontine = true;
                //减配不采用比重模式，直接一个一个循环过去，如果超过最小量了，则跳过的机制
                while (canminusnum > 0)
                {
                    boolcancontine = false;
                    for (var k = 0; k < datainfo.Count; k++)
                    {
                        if (canminusnum > 0)
                        {
                            var item = datainfo[k];
                            if (item.canminusnum > 0)
                            {
                                item.needminusnum++;
                                boolcancontine = true;
                                canminusnum--;
                            }
                        }
                    }
                    //都没有可以减少的了，则跳过
                    if (!boolcancontine) { break; }
                }
                //最终确认的减少的量
                _finalynum = datainfo.Sum(x => x.needminusnum);

                _logger.LogInformation($"对服务环境{modelid}计算出最终减配的数量总额为：{canminusnum}");

                foreach (var item in datainfo)
                {

                    if (item.needminusnum > 0)
                    {
                        var linux = await _modelHelper.ItemLinuxInfo(item.linuxid, _dbContext);
                        var comands = new List<PlanItem>();
                        var zindex = 0;

                        var plan = new PlanInfo();
                        plan.AppId = 0;
                        plan.CreateDate = DateTime.Now;
                        plan.Durtion = 0;
                        plan.ModelId = modelid;
                        plan.OrderModel = OrderModel.reduce;
                        plan.ServiceId = 0;
                        plan.AppId = 0;
                        plan.StoreId = 0;
                        plan.LinuxId = linux.Id;
                        plan.Version = 0;
                        plan.ProjectId = item.projectid;
                        _dbContext.Add(plan);
                        await _dbContext.SaveChangesAsync();
                        var planid = plan.Id;
                        #region 整理缩减的命令
                        var appstotal = nowruns.Where(x => x.LinuxId == item.linuxid).ToList();
                        if (appstotal != null && appstotal.Count > 0)
                        {
                            var finalnum = appstotal.Count > item.needminusnum ? item.needminusnum : appstotal.Count;
                            var appstodel = appstotal.GetRange(0, finalnum);
                            //数据库标记需要停止
                            foreach (var app in appstodel)
                            {
                                //标记数据库预停止
                                comands.Add(_buildPlanItemModel(_action: ActionState.sqlstop, _planid: planid, _zindex: zindex++, _appid: app.Id));
                            }
                            //休眠10次
                            comands.Add(_buildPlanItemModel(_action: ActionState.waitdely, _planid: planid, _zindex: zindex++));
                            foreach (var app in appstodel)
                            {
                                //停止docker
                                comands.Add(_buildPlanItemModel(_action: ActionState.dockerstop, _planid: planid, _command: $"docker stop {app.AppID}", _target: $"{app.AppID}", _zindex: zindex++, _strict: false));
                                //移除docker
                                comands.Add(_buildPlanItemModel(_action: ActionState.dockerrm, _planid: planid, _command: $"docker rm {app.AppID}", _target: $"{app.AppID}", _zindex: zindex++, _strict: false));
                            }
                        }
                        #endregion
                        if (comands.Count > 0)
                        {
                            await _dbContext.AddRangeAsync(comands.ToArray());
                            await _dbContext.SaveChangesAsync();
                            _logger.LogInformation($"提交了对服务{modelid}的减配{num}命令!");
                            _channelHelper.WritePlanCommand(plan.Id, plan.LinuxId);
                            _modelHelper.PlanRunTag(plan);
                        }

                    }
                }
            }
            else
            {
                throw new PasteException("没有需要减配的服务，对应的Linux上没有找到需要操作的服务!");
            }

            return _finalynum;
        }

        /// <summary>
        /// 停止某一个APP 会删除 会更新nginx
        /// </summary>
        /// <param name="_dbContext"></param>
        /// <param name="appid"></param>
        /// <returns></returns>
        public async Task<int> ContainerStop(IOpenPasteSpiderDbContext _dbContext, int appid)
        {
            var app = _dbContext.AppInfo.Where(x => x.Id == appid).AsNoTracking().FirstOrDefault();
            if (app == null || app == default)
            {
                throw new PasteException("不存在这个Docker服务，请确认！");
            }
            //datainfo里面就是每一个服务器需要减少的数量了
            var comands = new List<PlanItem>();
            //var commandGuid = Guid.NewGuid().ToString();
            var zindex = 0;
            var plan = new PlanInfo();
            plan.CreateDate = DateTime.Now;
            plan.Durtion = 0;
            plan.ModelId = 0;
            plan.OrderModel = OrderModel.stop;
            plan.ServiceId = 0;
            plan.AppId = app.Id;
            plan.StoreId = 0;
            plan.LinuxId = app.LinuxId;
            plan.Version = 0;
            plan.ProjectId = app.ProjectId;
            _dbContext.Add(plan);
            await _dbContext.SaveChangesAsync();
            var _planid = plan.Id;
            //标记数据库预停止
            comands.Add(_buildPlanItemModel(_action: ActionState.sqlstop, _planid: _planid, _zindex: zindex++, _appid: app.Id));
            //休眠10次
            comands.Add(_buildPlanItemModel(_action: ActionState.waitdely, _planid: _planid, _zindex: zindex++));
            //停止docker
            comands.Add(_buildPlanItemModel(_action: ActionState.dockerstop, _planid: _planid, _command: $"docker stop {app.AppID}", _target: $"{app.AppID}", _zindex: zindex++, _strict: false));
            //移除docker
            comands.Add(_buildPlanItemModel(_action: ActionState.dockerrm, _planid: _planid, _command: $"docker rm {app.AppID}", _target: $"{app.AppID}", _zindex: zindex++, _strict: false));
            //停止对应的docker在数据库的状态 后续可以复用，复用只是数据库的，容器需要重新run因为版本不一样了
            comands.Add(_buildPlanItemModel(_action: ActionState.sqlstop, _planid: _planid, _zindex: zindex++));
            if (comands.Count > 0)
            {
                await _dbContext.AddRangeAsync(comands.ToArray());
                await _dbContext.SaveChangesAsync();
                //ChannelData.WriteOrder(commandGuid);
                _channelHelper.WritePlanCommand(plan.Id, plan.LinuxId);
                _modelHelper.PlanRunTag(plan);
            }
            else
            {
                throw new PasteException("没有找到符合条件的减配对象，可能是服务和Linux之间设定了最小运行数量和需要减配的冲突了!");
            }
            return 1;
        }

        /// <summary>
        /// 拆分数字
        /// </summary>
        /// <param name="total"></param>
        /// <param name="args"></param>
        /// <returns></returns>
        private int[] _splitnumbertoarray(int total, int[] args)
        {
            var random = new Random();

            int[] newval = new int[args.Length];

            if (args.Length == 1)
            {
                newval[0] = total;
                return newval;
            }

            int size = args.Length;
            int hitnum = 0;
            //概率总数
            int countval = args.Sum() + 1;


            while (hitnum < total)
            {

                var activesize = (total - hitnum) / 100;
                if (activesize < 1) { activesize = 1; }

                //这里还要细分粒度

                //粒度如何计算。。。 。。 。
                //1000是如何计算的? 总数的1%%~
                if (activesize > 100) { activesize = 100; }

                //附加概率振幅

                var radint = random.Next(0, countval);

                //Console.WriteLine($"random is:{radint} activesize is:{activesize}");

                var tmpcount = 0;
                for (var k = 0; k < size; k++)
                {
                    if (args[k] > 0)
                    {
                        if (radint >= tmpcount && radint < (tmpcount + args[k]))
                        {

                            newval[k] += activesize;
                            break;
                        }
                        tmpcount += args[k];
                    }
                }

                hitnum += activesize;
            }
            return newval;
        }


        /// <summary>
        /// 运行 docker ps |grep appid 进行运行检测
        /// </summary>
        /// <param name="_dockerid"></param>
        /// <param name="linuxid"></param>
        /// <returns></returns>
        public async Task<(bool run, string message)> GetDockerPsGrep(string _dockerid, int linuxid)
        {

            var strbody = await RunCommandAsync($"docker ps | grep {_dockerid}", linuxid);

            if (strbody.Item1.Length > 0)
            {
                return (true, strbody.Item1);
            }
            return (false, strbody.Item1);
        }


        /// <summary>
        /// 读取容器的最后100行日志
        /// </summary>
        /// <param name="containerid"></param>
        /// <param name="linuxid"></param>
        /// <returns></returns>
        public async Task<(bool run, string message)> ReadContainerLog(string containerid, int linuxid)
        {

            var strbody = await RunCommandAsync($"docker logs --tail=100 {containerid}", linuxid);

            if (strbody.Item1.Length > 0)
            {
                return (true, strbody.Item1);
            }
            return (false, strbody.Item1);
        }


        /// <summary>
        /// 返回docker的Inspect信息
        /// </summary>
        /// <param name="dockername"></param>
        /// <param name="linuxid"></param>
        /// <returns></returns>
        public async Task<DockerInspectModel> GetDockerInspect(string dockername, int linuxid)
        {
            var strbody = await RunCommandAsync($"docker inspect {dockername}", linuxid);
            if (strbody.Item1.Length > 0)
            {
                try
                {
                    var obj = Newtonsoft.Json.JsonConvert.DeserializeObject<Newtonsoft.Json.Linq.JArray>(strbody.Item1);
                    //var js = obj as Newtonsoft.Json.Linq.JObject;
                    if (obj.Count > 0)
                    {
                        var dockerinfo = new DockerInspectModel();
                        var info = obj.First;
                        if (info != null)
                        {
                            if (info["Config"] != null)
                            {
                                if (info["Config"]["Image"] != null)
                                {
                                    dockerinfo.Image = info["Config"]["Image"].ToString();
                                }
                            }
                            if (info["Name"] != null)
                            {
                                dockerinfo.Name = info["Name"].ToString().Replace("/", "");
                            }
                            if (info["RestartCount"] != null)
                            {
                                int.TryParse(info["RestartCount"].ToString(), out var num);
                                dockerinfo.RestartCount = num;
                                //dockerinfo.Name = info["RestartCount"].ToString().Replace("/", "");
                            }
                            if (info["GraphDriver"] != null)
                            {
                                if (info["GraphDriver"]["Data"] != null)
                                {
                                    if (info["GraphDriver"]["Data"]["MergedDir"] != null)
                                    {
                                        dockerinfo.MergedDir = info["GraphDriver"]["Data"]["MergedDir"].ToString();
                                    }
                                }
                            }
                            if (info["NetworkSettings"] != null)
                            {
                                if (info["NetworkSettings"]["IPAddress"] != null)
                                {
                                    dockerinfo.IPAddress = info["NetworkSettings"]["IPAddress"].ToString();
                                }
                                //这里扩展，读取更多信息 可能是多个网卡的情况
                                if (info["NetworkSettings"]["Networks"] != null)
                                {
                                    var networks = info["NetworkSettings"]["Networks"];
                                    if (networks != null)
                                    {
                                        if (networks.HasValues)
                                        {
                                            var ipindex = 0;
                                            foreach (var i in networks.Children())
                                            {
                                                if (i.HasValues)
                                                {
                                                    foreach (var ih in i.Children())
                                                    {
                                                        if (ih.Type == JTokenType.Object)
                                                        {
                                                            if (ih.HasValues)
                                                            {
                                                                foreach (var ii in ih.Children())
                                                                {
                                                                    if (ii.Type == JTokenType.Property)
                                                                    {
                                                                        var jp = ii as JProperty;
                                                                        if (jp != null)
                                                                        {
                                                                            if (jp.Name == "IPAddress")
                                                                            {
                                                                                //获取到IP地址了！
                                                                                if (String.IsNullOrEmpty(dockerinfo.IPAddress))
                                                                                {
                                                                                    dockerinfo.IPAddress = jp.Value.ToString();
                                                                                }
                                                                                if (ipindex == 1)
                                                                                {
                                                                                    if (String.IsNullOrEmpty(dockerinfo.SecondIpAddress))
                                                                                    {
                                                                                        dockerinfo.SecondIpAddress = jp.Value.ToString();
                                                                                    }
                                                                                }
                                                                            }
                                                                        }
                                                                    }
                                                                }
                                                            }
                                                        }
                                                        ipindex++;
                                                    }
                                                }
                                            }
                                        }
                                        else
                                        {
                                            _logger.LogWarning("NetworkSettings.Networks !HasChildren ?");
                                        }
                                    }
                                    else
                                    {
                                        _logger.LogWarning("不存在 NetworkSettings.Networks?");
                                    }
                                }
                            }
                            if (info["State"] != null)
                            {
                                if (info["State"]["Status"] != null)
                                {
                                    dockerinfo.Status = info["State"]["Status"].ToString();
                                }
                            }
                            //HostConfig:PortBindings:80/tcp:HostPort
                            if (info["HostConfig"] != null)
                            {
                                if (info["HostConfig"]["PortBindings"] != null)
                                {
                                    var getbinds = info["HostConfig"]["PortBindings"];
                                    if (getbinds != null)
                                    {
                                        //新的模式，可以查询到非80的
                                        // 新的模式，兼容多端口模式
                                        if (getbinds.HasValues)
                                        {
                                            var ports = new List<int>();
                                            foreach (var one in getbinds.Children())
                                            {
                                                if (one.Type == JTokenType.Property)
                                                {
                                                    var p = (JProperty)one;
                                                    if (p.Value != null && p.Value.Type == JTokenType.Array)
                                                    {
                                                        foreach (var pa in (JArray)p.Value)
                                                        {
                                                            if (pa["HostPort"] != null)
                                                            {
                                                                if (int.TryParse(pa["HostPort"].ToString(), out var port))
                                                                {
                                                                    if (port != 0)
                                                                    {
                                                                        ports.Add(port);
                                                                    }
                                                                }
                                                            }
                                                        }
                                                    }
                                                }
                                            }
                                            dockerinfo.Ports = string.Join(',', ports.ToArray());
                                        }

                                    }
                                }
                            }
                        }
                        return dockerinfo;
                    }
                }
                catch (Exception exl)
                {
                    _logger.LogException(exl);
                }
            }
            return null;
        }

        /// <summary>
        /// 读取机器的流量信息
        /// </summary>
        /// <param name="linuxid"></param>
        /// <returns></returns>
        public async Task<(long NetIn, long NetOut)> GetLinuxNetwork(int linuxid)
        {
            //cat /proc/net/dev
            var bodyStr = await RunCommandAsync("cat /proc/net/dev", linuxid);
            if (!String.IsNullOrEmpty(bodyStr.Item1))
            {
                var lines = bodyStr.Item1.SplitByLine();
                if (lines != null)
                {
                    foreach (var item in lines)
                    {
                        if (item.IndexOf(":") > 0)
                        {
                            if (item.IndexOf("eth0") >= 0)
                            {
                                //  eth0: 40081087637 119582985    0    0    0     0          0         0 52340633651 145083682    0    0    0     0       0          0
                                if (item.Split(":")[0].Trim() == "eth0")
                                {
                                    var strs = item.Split(" ");
                                    var getstrs = strs.Where(x => !String.IsNullOrEmpty(x)).ToList();
                                    if (getstrs != null)
                                    {
                                        //Console.WriteLine($"network string :{string.Join("-",getstrs)}");
                                        long receive = 0;
                                        long transmit = 0;
                                        if (getstrs.Count > 1)
                                        {
                                            long.TryParse(getstrs[1], out receive);
                                        }
                                        if (getstrs.Count > 9)
                                        {
                                            long.TryParse(getstrs[9], out transmit);
                                        }
                                        return (receive, transmit);
                                    }
                                    break;
                                }
                            }
                        }
                    }
                }
            }
            return (0, 0);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="_action"></param>
        /// <param name="_command"></param>
        /// <param name="_target"></param>
        /// <param name="_planid"></param>
        /// <param name="_zindex"></param>
        /// <param name="_strict">必须成功</param>
        /// <param name="_appid"></param>
        /// <returns></returns>
        private PlanItem _buildPlanItemModel(
                ActionState _action = ActionState.custom,
                string _command = "",
                string _target = "",
                int _planid = 0,
                int _zindex = 0,
                bool _strict = true,
                int _appid = 0)
        {
            var item = new PlanItem
            {
                CreateDate = DateTime.Now,
                Command = _command,
                ActionType = _action,
                ActionResult = "",
                ExecState = RunState.waitrun,
                ExitCode = 0,
                StrictMode = _strict,
                PlanInfoId = _planid,
                RunIndex = _zindex,
                Target = _target,
                AppId = _appid
            };
            return item;
        }



        /// <summary>
        /// 查询某一个镜像是否存在
        /// </summary>
        /// <param name="imagename"></param>
        /// <param name="linuxid"></param>
        /// <returns></returns>
        public async Task<bool> CheckImageExists(string imagename, int linuxid)
        {
            var bodymodel = await RunCommandAsync("docker images --format \"{{.Repository}}:{{.Tag}}\"", linuxid);
            if (!String.IsNullOrEmpty(bodymodel.Item1))
            {
                var lines = bodymodel.Item1.SplitByLine();
                foreach (var item in lines)
                {
                    if (item.StartsWith(imagename) || item.StartsWith($"localhost/{imagename}"))
                    {
                        return true;
                    }
                }
            }
            return false;
        }


        /// <summary>
        /// 
        /// </summary>
        /// <param name="port"></param>
        /// <param name="linuxid"></param>
        /// <returns></returns>
        public async Task<string> GetDockerNameFromPort(int port, int linuxid)
        {
            var bodyString = await RunCommandAsync("docker ps -a | grep 0.0.0.0:" + port.ToString() + " | awk '{print $1}'", linuxid);
            return bodyString.Item1;
        }


        /// <summary>
        /// 返回当前服务器的运行的docker情况 CPU等情况
        /// </summary>
        /// <returns></returns>
        public async Task<List<DockerStateModel>> ReadDockerStatsAsync(int linuxid)
        {
            var statelist = new System.Collections.Generic.List<DockerStateModel>();
            var backinfo = await RunCommandAsync("docker stats --no-stream --format \"{\\\"idcode\\\":\\\"{{ .ID }}\\\",\\\"container\\\":\\\"{{ .Name }}\\\",\\\"memory\\\":\\\"{{ .MemUsage }}\\\",\\\"cpu\\\":\\\"{{ .CPUPerc }}\\\",\\\"pids\\\":\\\"{{ .PIDs }}\\\",\\\"net\\\":\\\"{{ .NetIO }}\\\"}\"", linuxid);
            var lines = backinfo.Item1.SplitByLine();// SplitByLine(backinfo.Item1);
            foreach (var item in lines)
            {
                // Console.WriteLine(item);
                if (item.StartsWith("{") && item.EndsWith("}"))
                {
                    var one = Newtonsoft.Json.JsonConvert.DeserializeObject<DockerStateModel>(item);
                    if (one != null && one != default)
                    {
                        one.appprefix = one.container.RejectNumChar();
                        statelist.Add(one);
                    }
                }
            }
            lines.Clear();
            lines = null;
            return statelist;
        }

        /// <summary>
        /// 通过ps -a查看所有容器运行状态
        /// </summary>
        /// <returns></returns>
        public async Task<List<DockerRunList>> ReadDockerRunningAsync(int linuxid)
        {
            var statelist = new System.Collections.Generic.List<DockerRunList>();
            var backinfo = await RunCommandAsync("docker ps --format \"{{ .Names }}&{{ .Status }}&{{ .Ports }}&{{ .ID }}\"", linuxid);
            if (!String.IsNullOrEmpty(backinfo.Item1))
            {
                var lines = backinfo.Item1.SplitToLines();// SplitByLine(backinfo.Item1);
                foreach (var item in lines)
                {
                    if (item.Contains("&"))
                    {
                        var splitstrs = item.Split("&");
                        if (splitstrs.Length >= 3)
                        {
                            var one = new DockerRunList();
                            one.container = splitstrs[0];
                            one.runstate = splitstrs[1];
                            one.ports = splitstrs[2];
                            one.appid = splitstrs[3];
                            one.isrunning = true;
                            if (one.runstate.StartsWith("Exited") || one.runstate.StartsWith("Restart"))
                            {
                                one.isrunning = false;
                            }
                            statelist.Add(one);
                        }
                    }
                }
            }
            return statelist;
        }


        /// <summary>
        /// 读取服务器的CPU和内存信息
        /// </summary>
        /// <returns></returns>
        public async Task<LinuxState> GetLinuxState(int linuxid)
        {
            //free 
            var bodyStr = await RunCommandAsync("top -bn 1 -i -c", linuxid);
            if (!String.IsNullOrEmpty(bodyStr.Item1))
            {
                var one = new LinuxState();
                var linewords = bodyStr.Item1.SplitByLine();
                foreach (var item in linewords)
                {
                    if (item.StartsWith("top") || item.Contains("average"))
                    {
                        var matchs = new System.Text.RegularExpressions.Regex(@"average: (?<one>[0-9,\.]{1,6}), (?<two>[0-9,\.]{1,6}), (?<three>[0-9,\.]{1,6})").Match(item);
                        if (matchs.Success)
                        {
                            double.TryParse(matchs.Groups["one"].Value, out var cpu1);
                            one.CPU1 = (decimal)cpu1;
                            double.TryParse(matchs.Groups["two"].Value, out var cpu2);
                            one.CPU2 = (decimal)cpu2;
                            double.TryParse(matchs.Groups["three"].Value, out var cpu3);
                            one.CPU3 = (decimal)cpu3;
                        }
                        continue;
                    }
                    if (item.StartsWith("%C") || item.StartsWith("Cpu"))
                    {
                        var match = new System.Text.RegularExpressions.Regex(@":  (?<used>[0-9,\.]{1,5}) us").Match(item);
                        if (match.Success)
                        {
                            double.TryParse(match.Groups["used"].Value, out var cpu);
                            one.CPUUsed = (decimal)cpu;
                        }
                        continue;
                    }

                    if (item.StartsWith("KiB Mem"))
                    {
                        var matchs = new System.Text.RegularExpressions.Regex(@":([ ]{1,3})(?<one>[0-9]{1,11}) total,([ ]{1,3})(?<two>[0-9]{1,11}) free,([ ]{1,3})(?<three>[0-9]{1,11}) used").Match(item);
                        if (matchs.Success)
                        {
                            double.TryParse(matchs.Groups["one"].Value, out var total);
                            one.MemoryTotal = (decimal)total / 1024;
                            double.TryParse(matchs.Groups["two"].Value, out var cpu2);
                            one.MemoryFree = (decimal)cpu2 / 1024;
                            double.TryParse(matchs.Groups["three"].Value, out var cpu3);
                            one.MemoryUsed = (decimal)cpu3 / 1024;
                        }
                        continue;
                    }
                    //ubuntu的内存
                    if (item.StartsWith("MiB Mem"))
                    {
                        var matchs = new System.Text.RegularExpressions.Regex(@":([ ]{1,8})(?<one>[0-9,\.]{1,11}) total,([ ]{1,8})(?<two>[0-9,\.]{1,11}) free,([ ]{1,8})(?<three>[0-9,\.]{1,11}) used").Match(item);
                        if (matchs.Success)
                        {
                            double.TryParse(matchs.Groups["one"].Value, out var total);
                            one.MemoryTotal = (decimal)total;
                            double.TryParse(matchs.Groups["two"].Value, out var cpu2);
                            one.MemoryFree = (decimal)cpu2;
                            double.TryParse(matchs.Groups["three"].Value, out var cpu3);
                            one.MemoryUsed = (decimal)cpu3;
                        }
                        continue;
                    }
                }
                linewords.Clear();
                return one;
            }
            return null;
        }

        /// <summary>
        /// 服务器内存情况
        /// </summary>
        /// <returns></returns>
        public async Task<ModelLinuxMemoryInfo> ReadLinuxMemoryAsync(int linuxid)
        {
            //free 
            var bodyStr = await RunCommandAsync("free -t -h", linuxid);
            if (!String.IsNullOrEmpty(bodyStr.Item1))
            {
                //                total used        free shared  buff / cache   available
                //  Mem:           3.5G        950M        2.0G        107M        580M        2.2G
                //  Swap:            0B          0B          0B
                //Total: 3.5G        950M        2.0G

                //var one = new LinuxState();
                var info = new ModelLinuxMemoryInfo();

                var linewords = bodyStr.Item1.SplitByLine();
                foreach (var item in linewords)
                {
                    if (item.StartsWith("Mem:"))
                    {
                        var strs = item.Split(' ');
                        var zindex = -1;
                        foreach (var one in strs)
                        {
                            if (!String.IsNullOrEmpty(one))
                            {
                                zindex++;
                                switch (zindex)
                                {
                                    case 1:
                                        if (one.IndexOf("G") >= 0)
                                        {
                                            double.TryParse(one.Replace("G", ""), out var ival);
                                            info.Total = Math.Round(ival * 1024, 2);
                                        }
                                        else
                                        {
                                            double.TryParse(one.Replace("M", ""), out var ival);
                                            info.Total = Math.Round(ival, 2);
                                        }
                                        break;
                                    case 2:
                                        if (one.IndexOf("G") >= 0)
                                        {
                                            double.TryParse(one.Replace("G", ""), out var ival);
                                            info.Used = Math.Round(ival * 1024, 2);
                                        }
                                        else
                                        {
                                            double.TryParse(one.Replace("M", ""), out var ival);
                                            info.Used = Math.Round(ival, 2);
                                        }
                                        break;
                                    case 3:
                                        if (one.IndexOf("G") >= 0)
                                        {
                                            double.TryParse(one.Replace("G", ""), out var ival);
                                            info.Free = Math.Round(ival * 1024, 2);
                                        }
                                        else
                                        {
                                            double.TryParse(one.Replace("M", ""), out var ival);
                                            info.Free = Math.Round(ival, 2);
                                        }
                                        break;
                                    case 4:
                                        if (one.IndexOf("G") >= 0)
                                        {
                                            double.TryParse(one.Replace("G", ""), out var ival);
                                            info.Shared = Math.Round(ival * 1024, 2);
                                        }
                                        else
                                        {
                                            double.TryParse(one.Replace("M", ""), out var ival);
                                            info.Shared = Math.Round(ival, 2);
                                        }
                                        break;
                                    case 5:
                                        if (one.IndexOf("G") >= 0)
                                        {
                                            double.TryParse(one.Replace("G", ""), out var ival);
                                            info.Buff = Math.Round(ival * 1024, 2);
                                        }
                                        else
                                        {
                                            double.TryParse(one.Replace("M", ""), out var ival);
                                            info.Buff = Math.Round(ival, 2);
                                        }
                                        break;
                                    case 6:
                                        if (one.IndexOf("G") >= 0)
                                        {
                                            double.TryParse(one.Replace("G", ""), out var ival);
                                            info.Available = Math.Round(ival * 1024, 2);
                                        }
                                        else
                                        {
                                            double.TryParse(one.Replace("M", ""), out var ival);
                                            info.Available = Math.Round(ival, 2);
                                        }
                                        break;
                                }
                            }
                        }


                    }
                    //if (item.StartsWith("Total"))
                    //{

                    //    break;
                    //}

                }

                linewords.Clear();

                return info;
            }
            return null;

        }

        /// <summary>
        /// 返回本机占用的端口信息
        /// </summary>
        /// <returns></returns>
        public async Task<List<int>> GetUsedPorts(int linuxid)
        {

            //yum install net-tools
            var ports = new List<int>();

            //windows netstat -ano
            var backinfo = await RunCommandAsync("netstat -tunlp", linuxid);

            var lines = backinfo.Item1.SplitByLine();// SplitByLine(backinfo.Item1);


            foreach (var item in lines)
            {
                //Console.WriteLine($"used port line:{item}");
                //tcp        0      0 0.0.0.0:80              0.0.0.0:*               LISTEN      105/nginx: master p 
                // TCP [::]:12000
                // :::8005
                var splitword = item.Split(" ");
                if (splitword.Length >= 5)
                {
                    foreach (var portline in splitword)
                    {
                        if (!String.IsNullOrEmpty(portline))
                        {
                            var word = portline;
                            if (word.StartsWith(":::"))
                            {
                                if (int.TryParse(word.Replace(":::", ""), out var port))
                                {
                                    if (port > 0)
                                    {
                                        ports.Add(port);
                                    }
                                }
                                continue;
                            }
                            else if (word.Contains("]:"))
                            {
                                if (int.TryParse(word.Split(":")[1], out var port))
                                {
                                    if (port > 0)
                                    {
                                        ports.Add(port);
                                    }
                                }
                                continue;
                            }
                            else
                            {
                                if (word.IndexOf(":") > 0)
                                {
                                    if (int.TryParse(word.Split(":")[1], out var port))
                                    {
                                        if (port > 0)
                                        {
                                            ports.Add(port);
                                        }
                                    }
                                    continue;
                                }
                            }
                        }
                    }
                }
            }

            ports.Sort();
            return ports;

        }

        /// <summary>
        /// 执行单词command命令
        /// </summary>
        /// <param name="command"></param>
        /// <param name="linuxid"></param>
        /// <returns></returns>
        public async Task<(string msg, bool ok, int code)> RunCommandAsync(string command, int linuxid)
        {
            var result = await _linkHelper.RunSingleCommandAsync(linuxid, command);
            return result;
        }


        public void Dispose()
        {

        }
    }
}
